package cn.clq.ymcc.service.impl;

import cn.clq.ymcc.constants.CourseConstants;
import cn.clq.ymcc.doc.CourseDoc;
import cn.clq.ymcc.domain.*;
import cn.clq.ymcc.dto.CourseDto;
import cn.clq.ymcc.dto.CourseOnSendMessageDto;
import cn.clq.ymcc.dto.UserDto;
import cn.clq.ymcc.feign.CourseSearchFeignClient;
import cn.clq.ymcc.feign.LoginFeignClient;
import cn.clq.ymcc.feign.SearchMediaFilesFeignClient;
import cn.clq.ymcc.mapper.CourseMapper;
import cn.clq.ymcc.mq.CourseOnSendProducer;
import cn.clq.ymcc.result.JSONResult;
import cn.clq.ymcc.service.*;
import cn.clq.ymcc.util.AssertUtil;
import cn.clq.ymcc.util.StrUtils;
import cn.clq.ymcc.vo.CourseDetailInfoVo;
import cn.clq.ymcc.vo.CourseItemVo;
import cn.clq.ymcc.vo.CourseOrderVo;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author stone.chen
 * @since 2022-07-21
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {

    @Autowired
    private ITeacherService teacherService;
    @Autowired
    private ICourseDetailService courseDetailService;
    @Autowired
    private ICourseResourceService courseResourceService;
    @Autowired
    private ICourseMarketService courseMarketService;
    @Autowired
    private ICourseTeacherService courseTeacherService;
    @Autowired
    private ICourseTypeService courseTypeService;
    @Autowired
    private ICourseSummaryService courseSummaryService;
    @Autowired
    private ICourseChapterService courseChapterService;
    @Autowired
    private CourseSearchFeignClient courseSearchFeignClient;
    @Autowired
    private LoginFeignClient loginFeignClient;
    @Autowired
    private CourseOnSendProducer courseOnSendProducer;
    @Autowired
    private SearchMediaFilesFeignClient searchMediaFilesFeignClient;

    /**
     * @description: 保存课程
     * 1.参数校验
     * 2.课程不能被注册
     * 3.保存数据
     * 保存课程
     * 保存课程详情
     * 保存课程销售
     * 保存课程讲师
     * 保存课程资源
     * 初始化课程统计
     *
     * 课程类型表中的课程数+1
     * @param: dto
     * @return: void
     * @author clqlongbao
     * @date: 2022/7/22 0:29
     */
    @Override
    @Transactional
    public void save(CourseDto dto) {
        Course course = dto.getCourse();
        CourseDetail courseDetail = dto.getCourseDetail();
        CourseMarket courseMarket = dto.getCourseMarket();
        CourseResource courseResource = dto.getCourseResource();
        List<Long> teacherIds = dto.getTeacharIds();
        //1.使用JSR303参数校验
        //2.判断是否存在同名课程
        Course courseTmp = selectByName(course.getName());
        AssertUtil.isNull(courseTmp,CourseConstants.COURSE_EXIST);//同名课程已经存在
        //3.保存数据
        //重要数据的初始化要放在service中进行，防止数据被篡改
        //保存课程
        course.setStatus(CourseConstants.COURSE_STATE_OFFLINE);//0,下架
        //@TODO 保存操作员工
        course.setLoginId(1L);
        course.setLoginUserName("yhptest1");
        List<Teacher> teachers = teacherService.selectBatchIds(teacherIds);
        // 整成stream
        StringBuilder sb = new StringBuilder();
        teachers.forEach(teacher -> {
            sb.append(teacher.getName()).append(",");
        });
        course.setTeacherNames(sb.toString());
        insert(course);
        //保存课程详情
        saveCourseDetail(course, courseDetail);
        //保存课程销售
        saveCourseMarket(course, courseMarket);
        //保存课程讲师
        saveCourseTeacher(course, teacherIds);
        //保存课程资源
        saveCourseResource(course, courseResource);
        //初始化课程统计
        initCourseSummary(course);
        //课程类型表中该类型的课程数+1
        CourseType type = courseTypeService.selectById(course.getCourseTypeId());
        type.setTotalCount(type.getTotalCount() + 1);
        courseTypeService.updateById(type);
    }

    private void initCourseSummary(Course course) {
        CourseSummary courseSummary = new CourseSummary();
        courseSummary.setId(course.getId());
        courseSummaryService.insert(courseSummary);
    }

    private void saveCourseResource(Course course, CourseResource courseResource) {
        courseResource.setCourseId(course.getId());
        courseResourceService.insert(courseResource);
    }

    private void saveCourseTeacher(Course course, List<Long> teacherIds) {
        teacherIds.forEach(id->{
            CourseTeacher courseTeacher = new CourseTeacher();
            courseTeacher.setCourseId(course.getId());
            courseTeacher.setTeacherId(id);
            courseTeacherService.insert(courseTeacher);
        });
    }

    private void saveCourseMarket(Course course, CourseMarket courseMarket) {
        courseMarket.setId(course.getId());
        courseMarketService.insert(courseMarket);
    }

    private void saveCourseDetail(Course course, CourseDetail courseDetail) {
        courseDetail.setId(course.getId());
        courseDetailService.insert(courseDetail);
    }

    private Course selectByName(String name) {
        Wrapper<Course> tWrapper = new EntityWrapper<Course>();
        tWrapper.eq("name",name);
        return selectOne(tWrapper);
    }

    /**
     * @description: 课程发布
     * 1.根据课程Id查询数据库，判断课程是否存在
     * 2.修改课程状态为上架，填写上架时间
     * 3.保存课程相关内容到Es
     *  3.1 为service-search编写feign接口ymcc-api-search
     *  3.2 在课程微服务中，依赖ymcc-api-search
     *  3.3 设置需要保存到Es的字段
     *  3.4 使用feign远程调用search将课程保存到Es中
     * @param: courseId
     * @return: void
     * @author clqlongbao
     * @date: 2022/7/24 15:02
     */
    @Override
    public void onLineCourse(Long courseId) {
        //1.根据课程Id查询数据库，判断课程是否存在
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,CourseConstants.COURSE_NOT_EXIST);//课程不存在
        //2.修改课程状态为上架，填写上架时间
        AssertUtil.isTrue(course.getStatus()==CourseConstants.COURSE_STATE_OFFLINE,CourseConstants.COURSE_STATE_WRONG);//课程状态错误
        course.setStatus(CourseConstants.COURSE_STATE_ONLINE);
        course.setOnlineTime(new Date());
        updateById(course);
        //3.保存课程相关内容到Es
        // 3.1 为service-search编写feign接口ymcc-api-search
        // 3.2 在course微服务中依赖ymcc-api-search
        CourseType type = courseTypeService.selectById(course.getCourseTypeId());
        CourseMarket courseMarket = courseMarketService.selectById(courseId);
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);
        CourseDoc doc = new CourseDoc();
        //3.3 设置需要保存到Es的字段
        BeanUtils.copyProperties(course,doc);
        BeanUtils.copyProperties(courseMarket,doc);
        BeanUtils.copyProperties(courseSummary,doc);
        doc.setCourseTypeName(type.getName());//这里要单独赋值,不然会覆盖掉course的name
        String chargeName = courseMarket.getCharge() == 1 ? "免费" : "付费";
        doc.setChargeName(chargeName);
        //3.4 使用feign远程调用search将课程保存到Es中
        JSONResult result = courseSearchFeignClient.saveCourse(doc);
        AssertUtil.isTrue(result.isSuccess(),"课程发布失败");

        //课程发布成功之后
        //4.调用user微服务查询t_user表的用户数据，得到指定用户信息
        System.out.println("课程发布成功，调用生产者发布消息");
        CourseOnSendMessageDto messageDto = new CourseOnSendMessageDto();
        messageDto.setTitle("新课程发布："+doc.getName());
        messageDto.setContent("亲，您关注的"+
                doc.getCourseTypeName()+
                "相关的课程"+doc.getName()+
                "已上线，尝鲜地址：<a href='localhost:6002/list.html?coursId="+doc.getId()+"'");
        messageDto.setUsers(Arrays.asList(
                new UserDto(1L,"13772019981"),
                new UserDto(2L,"19141324481")
        ));
        courseOnSendProducer.sendMessage(messageDto);
    }

    /**
     * @description: 课程下架
     * 1.根据课程Id查询数据库，判断课程是否存在
     * 2.修改课程状态为下架，填写下架时间
     * 3.将课程从Es中删除
     *  3.1 从Es中查询该课程是否存在
     *  3.2 使用feign远程调用search将课程从es中删除
     * @param: courseId
     * @return: void
     * @author clqlongbao
     * @date: 2022/7/25 16:46
     */
    @Override
    public void offLineCourse(Long courseId) {
        //1.根据课程Id查询数据库，判断课程是否存在
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,CourseConstants.COURSE_NOT_EXIST);//课程不存在
        //2.修改课程状态为下架，填写下架时间
        AssertUtil.isTrue(course.getStatus()==CourseConstants.COURSE_STATE_ONLINE,CourseConstants.COURSE_STATE_WRONG);//课程状态错误
        course.setStatus(CourseConstants.COURSE_STATE_OFFLINE);
        updateById(course);
        //3.将课程从Es中删除
        CourseDoc doc = new CourseDoc();
        doc.setId(course.getId());
        //3.1从Es中查询该课程是否存在

        //3.2使用feign远程调用search将课程从es中删除
        JSONResult jsonResult = courseSearchFeignClient.deleteCourse(doc);
        AssertUtil.isTrue(jsonResult.isSuccess(),"课程下架失败");
    }


    /**
     * @description:  课程详情页面查询课程详情
     *     private CourseMarket courseMarket;
     *     private Course course;
     *     private List<CourseChapter> courseChapters;
     *     private List<Teacher> teachers;
     *     private CourseSummary courseSummary;
     *     private CourseDetail courseDetail;
     * @param: courseId
     * @return: cn.clq.ymcc.vo.CourseDetailInfoVo
     * @author clqlongbao
     * @date: 2022/7/27 18:11
     */
    @Override
    public CourseDetailInfoVo searchCourseDetail(Long courseId) {
        AssertUtil.isNotNull(courseId,"已阻止非法请求");
        //查询所有前端需要的数据
        //课程
        Course course = selectById(courseId);
        AssertUtil.isNotNull(course,"已阻止非法请求");
        //未上架的课程无法观看视频，防止用户通过地址栏猜数据查到数据库
        boolean isOnline = course.getStatus() == CourseConstants.COURSE_STATE_ONLINE;
        AssertUtil.isTrue(isOnline,"已阻止非法请求");
        //销售
        CourseMarket courseMarket = courseMarketService.selectById(courseId);
        //统计
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);
        //详情
        CourseDetail courseDetail = courseDetailService.selectById(courseId);
        //老师
        Wrapper<CourseTeacher> teacherWrapper = new EntityWrapper<>();
        teacherWrapper.eq("course_id",courseId);
        List<CourseTeacher> courseTeachers = courseTeacherService.selectList(teacherWrapper);
        List<Long> teachersId = courseTeachers.stream().map(CourseTeacher::getTeacherId).collect(Collectors.toList());
        List<Teacher> teachers = teacherService.selectBatchIds(teachersId);
        //章节
        Wrapper<CourseChapter> chapterWrapper = new EntityWrapper<>();
        chapterWrapper.eq("course_id",courseId);
        List<CourseChapter> courseChapters = courseChapterService.selectList(chapterWrapper);
        //章节还需要查询每个章节下面的所有视频，调用media微服务查询
        AssertUtil.isNotNull(courseChapters,"抱歉，该课程暂时没有可观看视频");
        courseChapters.forEach(courseChapter -> {
            JSONResult jsonResult = searchMediaFilesFeignClient.searchMediaFiles(courseChapter.getId());
            if(jsonResult.isSuccess() && jsonResult.getData()!=null){
                //反序列化
                List<MediaFile> mediaFiles = JSONObject.parseArray(jsonResult.getData().toString(), MediaFile.class);
                //将返回结果的视频连接置空，在点击播放视频的时候再配合权限去查询地址
                mediaFiles.forEach(mediaFile -> mediaFile.setFileUrl(null));
                courseChapter.getMediaFiles().addAll(mediaFiles);
            }
        });
        //封装数据返回给前端
        CourseDetailInfoVo vo = new CourseDetailInfoVo();
        vo.setCourse(course);
        vo.setCourseChapters(courseChapters);
        vo.setCourseDetail(courseDetail);
        vo.setCourseMarket(courseMarket);
        vo.setCourseSummary(courseSummary);
        vo.setTeachers(teachers);
        return vo;
    }


    /**
     * @description: 查询课程结算界面的所需数据
     * 1.参数校验
     * 2.业务判断,有没有传重复的课程
     * 3.查询每一行数据并封装
     * 4.封装整个页面的数据
     * @param: courseIds
     * @return: cn.clq.ymcc.result.JSONResult
     * @author clqlongbao
     * @date: 2022/7/29 11:02
     */
    @Override
    public CourseOrderVo searchCourseOrderInfo(String courseIds) {
        //1.参数校验，参数不能为空
        AssertUtil.isNotEmpty(courseIds,"已阻止非法请求,参数不能为空");
        //2.业务判断,课程必须存在，课程必须上架，课程不能重复
        List<Long> ids = StrUtils.splitStr2LongArr(courseIds, ",");
        //转为set自动去重
        Set<Long> setIds = ids.stream().collect(Collectors.toSet());
        AssertUtil.isTrue(ids.size() == setIds.size(),"已阻止非法请求，课程重复");

        List<CourseItemVo> courseInfos = new ArrayList<>();
        BigDecimal totalAmount = new BigDecimal(0);

        for (Long id : setIds) {
            Course course = selectById(id);
            AssertUtil.isNotNull(course,"已阻止非法请求,课程不存在");
            AssertUtil.isTrue(course.getStatus() == CourseConstants.COURSE_STATE_ONLINE,"已阻止非法请求，课程未上架");
            //3.封装每一行的数据
            CourseMarket courseMarket = courseMarketService.selectById(id);
            CourseItemVo courseItemVo = new CourseItemVo(course, courseMarket);
            //4.封装整个页面的数据
            courseInfos.add(courseItemVo);
            totalAmount = totalAmount.add(courseMarket.getPrice());
        }
        return new CourseOrderVo(courseInfos,totalAmount);
    }


}
